package org.zero.onlinesubject.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.zero.onlinesubject.common.constant.RedisConstant;
import org.zero.onlinesubject.common.vo.ChapterVo;
import org.zero.onlinesubject.entity.Chapter;
import org.zero.onlinesubject.entity.ChapterVideo;
import org.zero.onlinesubject.mapper.ChapterVideoMapper;
import org.zero.onlinesubject.mapper.CourseMapper;
import org.zero.onlinesubject.service.ChapterService;
import org.zero.onlinesubject.mapper.ChapterMapper;
import org.springframework.stereotype.Service;
import org.zero.onlinesubject.service.SystemFileService;
import org.zero.onlinesubject.utils.IDWorker;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 *
 */
@Service
public class ChapterServiceImpl extends ServiceImpl<ChapterMapper, Chapter>
    implements ChapterService{

    @Resource
    private PlatformTransactionManager transactionManager;

    @Resource
    private ChapterMapper chapterMapper;

    @Resource
    private SystemFileService systemFileService;

    @Resource
    private ThreadService threadService;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private ChapterVideoMapper chapterVideoMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private Executor taskExecutor;

    @Override
    public List<ChapterVo> getChapterByCourseId(String courseId) throws ExecutionException, InterruptedException {
        // 更新课程的viewCount
        threadService.updateCourseViewCount(courseMapper,courseId);
        // 先查询缓存
        List<Chapter> chapterList = null;
        String body = stringRedisTemplate.opsForValue().get(RedisConstant.COURSE_CHAPTER_PREFIX + courseId);
        if(StringUtils.isNullOrEmpty(body)){
            chapterList = chapterMapper.selectByClsID(courseId);
            stringRedisTemplate.opsForValue().set(RedisConstant.COURSE_CHAPTER_PREFIX + courseId,JSON.toJSONString(chapterList),30,TimeUnit.MINUTES);
        }else{
            chapterList = JSON.parseObject(body,new TypeReference<List<Chapter>>(){});
        }

        if(chapterList.isEmpty()){
            return null;
        }
        List<String> cptIds = chapterList.stream().map(Chapter::getChapterid).collect(Collectors.toList());

        String idStr = stringRedisTemplate.opsForValue().get(RedisConstant.CHAPTER_VIDEO_ID_PREFIX+courseId);
        List<String> diff = null;
        List<ChapterVideo> chapterVideos = new ArrayList<>();
        CompletableFuture<Void> f3 = null;
        // 走缓存获取数据
        if(!StringUtils.isNullOrEmpty(idStr)){
            String[] ids = idStr.split(",");
            diff = findDiff(ids, cptIds);

            if(diff.size() == 0){
                String jsonBody = stringRedisTemplate.opsForValue().get(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId);
                chapterVideos = JSON.parseObject(jsonBody,new TypeReference<List<ChapterVideo>>(){});
            }else{
                CompletableFuture<List<ChapterVideo>> f1 = CompletableFuture.supplyAsync(() -> {
                    String strJson = stringRedisTemplate.opsForValue().get(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId);
                    if(StringUtils.isNullOrEmpty(strJson)){
                        return null;
                    }
                    return JSON.parseObject(strJson,new TypeReference<List<ChapterVideo>>(){});
                }, taskExecutor);

                List<String> finalDiff = diff;
                CompletableFuture<List<ChapterVideo>> f2 = CompletableFuture.supplyAsync(() -> chapterVideoMapper.selectByIds(finalDiff), taskExecutor);

                try {
                    chapterVideos.addAll(f2.get(3,TimeUnit.SECONDS));
                    chapterVideos.addAll(f1.get(3,TimeUnit.SECONDS));
                } catch (InterruptedException | ExecutionException | TimeoutException e) {
                    e.printStackTrace();
                    log.error(e.getMessage());
                }
                // 将数据存放到Redis中
                List<String> finalDiff1 = diff;
                List<ChapterVideo> finalChapterVideos = chapterVideos;
                f3 = CompletableFuture.runAsync(() -> {
                    finalDiff1.addAll(List.of(ids));
                    String idValue = String.join(",", finalDiff1);
                    stringRedisTemplate.opsForValue().set(RedisConstant.CHAPTER_VIDEO_ID_PREFIX+courseId,idValue,30,TimeUnit.MINUTES);
                    stringRedisTemplate.opsForValue().set(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId,JSON.toJSONString(finalChapterVideos),30,TimeUnit.MINUTES);
                }, taskExecutor);
            }
        }
        else{
            chapterVideos = chapterVideoMapper.selectByIds(cptIds);
            List<ChapterVideo> finalChapterVideos1 = chapterVideos;
            f3 = CompletableFuture.runAsync(()->{
                String idValue = String.join(",", cptIds);
                stringRedisTemplate.opsForValue().set(RedisConstant.CHAPTER_VIDEO_ID_PREFIX+courseId,idValue,30,TimeUnit.MINUTES);
                stringRedisTemplate.opsForValue().set(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId,JSON.toJSONString(finalChapterVideos1),30,TimeUnit.MINUTES);
            },taskExecutor);
        }
        Map<String, List<ChapterVideo>> map = new HashMap<>();
        for (ChapterVideo chapterVideo : chapterVideos) {
            if(map.containsKey(chapterVideo.getChapterid())){
                map.get(chapterVideo.getChapterid()).add(chapterVideo);
            }else{
                ArrayList<ChapterVideo> list = new ArrayList<>();
                list.add(chapterVideo);
                map.put(chapterVideo.getChapterid(), list);
            }
        }

        List<ChapterVo> list = chapterList.stream().map(chapter -> {
            ChapterVo chapterVo = new ChapterVo();
            BeanUtils.copyProperties(chapter, chapterVo);
//            System.out.println(map.get(chapter.getChapterid()));
            chapterVo.setVideoList((map.get(chapter.getChapterid())));
            return chapterVo;
        }).collect(Collectors.toList());
        if(f3!=null){
            f3.get();
        }
        return list;
    }

    private List<String> findDiff(String[] ids,List<String> cptIds){
        Map<String,Integer> diffMap = new HashMap<>();
        for (String id : ids) {
            diffMap.put(id,1);
        }
        List<String> diffId = new ArrayList<>();
        for (String cptId : cptIds) {
            if(!diffMap.containsKey(cptId)){
                diffId.add(cptId);
            }
        }
        return diffId;
    }

    @Override
    public Boolean delCosChapter(String chapterId) {
        TransactionStatus transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            removeById(chapterId);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("删除章节信息失败: {}",e);
            transactionStatus.setRollbackOnly();
        }
        if(!transactionStatus.isRollbackOnly()){
            try {
                systemFileService.deleteFileByBizID(List.of(chapterId));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("删除章节信息失败: {}",e);
                transactionManager.rollback(transactionStatus);
                return false;
            }
            transactionManager.commit(transactionStatus);
        }
        return true;
    }

    @Override
    public void updateLikeCount(String courseId) {
        threadService.updateCourseLikeCount(courseMapper,courseId);
    }

    @Override
    public Integer getLastOrderByCourseId(String courseId) {
        QueryWrapper<Chapter> wrapper = new QueryWrapper<>();
        wrapper.select("`Order`").eq("CourseID",courseId).orderByDesc("`Order`").last(" limit 1");
        Chapter chapter = chapterMapper.selectOne(wrapper);
        return chapter == null ? 1 : chapter.getOrder();
    }

    @Override
    public Boolean addChapterInfo(Chapter chapter) {
        String courseid = chapter.getCourseid();
        String cpid = IDWorker.createCPID();
        chapter.setChapterid(cpid);
        Integer order = getLastOrderByCourseId(courseid);
        chapter.setOrder(order+1);

        boolean flag = chapterMapper.insertOne(chapter)!=0;

        String body = stringRedisTemplate.opsForValue().get(RedisConstant.COURSE_CHAPTER_PREFIX + courseid);
        if(!StringUtils.isNullOrEmpty(body) && flag){
            List<Chapter> list = JSON.parseObject(body, new TypeReference<List<Chapter>>() {});
            list.add(chapter);
            stringRedisTemplate.opsForValue().set(RedisConstant.COURSE_CHAPTER_PREFIX+courseid,JSON.toJSONString(list),2,TimeUnit.HOURS);
        }

        return flag;
    }

    @Override
    public Boolean delChapter(String courseId,String chapterId) {
        QueryWrapper<Chapter> wrapper = new QueryWrapper<>();
        wrapper.eq("ChapterID",chapterId).eq("CourseID",courseId).last(" limit 1");
        boolean flag = remove(wrapper);
        if(!flag) return false;
        stringRedisTemplate.delete(RedisConstant.COURSE_CHAPTER_PREFIX + courseId);
        stringRedisTemplate.delete(RedisConstant.CHAPTER_VIDEO_PREFIX + courseId);
        stringRedisTemplate.delete(RedisConstant.CHAPTER_VIDEO_ID_PREFIX + courseId);
        return true;
    }
}
